/* 
Scenic Graphics Library
Copyright (C) 2007 Jouni Tulkki

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USApackage scenic;
*/

#include <string.h>

#ifndef WIN32
#include <pthread.h>
#endif

#include "common.h"
#include "Util.h"


using namespace Scenic::Renderer;


short convert_32f_16f(float f)
{
	unsigned int i = *(unsigned int*)&f;
	int sign = (i >> 31) & 1;
	int exp = (i >> 23) & 0xff;
	int mantissa = i;

	exp = exp - 127 + 15;

	if(exp < 0)
		exp = 0;
	else if(exp > 31)
		exp = 31;

	mantissa = (mantissa >> 13) & 0x3ff;
	return (sign << 15) | (exp << 10) | mantissa;
}

bool Scenic::Renderer::convert(char *src, int srcPitch, Format srcFormat, char *dest, int dstPitch, Format dstFormat,
	int width, int height)
{
	int xx, yy;
	
	if(dstFormat == srcFormat)
	{
		int size = Device::getFormatSize(srcFormat);
		
		for(yy = 0; yy < height; yy++)
		{
			memcpy(dest, src, width * size);
			dest += dstPitch;
			src += srcPitch;
		}
	}
	else if(dstFormat == FMT_A8R8G8B8 && srcFormat == FMT_R8G8B8)
	{
		for(yy = 0; yy < height; yy++)
		{
			for(xx = 0; xx < width; xx++)
			{
				dest[xx * 4] = src[xx * 3];
				dest[xx * 4 + 1] = src[xx * 3 + 1];
				dest[xx * 4 + 2] = src[xx * 3 + 2];
				dest[xx * 4 + 3] = (char)255;
			}
			dest += dstPitch;
			src += srcPitch;
		}
	}
	else if(dstFormat == FMT_A8R8G8B8 && srcFormat == FMT_R8G8B8A8)
	{
		for(yy = 0; yy < height; yy++)
		{
			for(xx = 0; xx < width; xx++)
			{
				dest[xx * 4] = src[xx * 4 + 1];
				dest[xx * 4 + 1] = src[xx * 4 + 2];
				dest[xx * 4 + 2] = src[xx * 4 + 3];
				dest[xx * 4 + 3] = src[xx * 4];
			}
			dest += dstPitch;
			src += srcPitch;
		}
	}
	else if(dstFormat == FMT_R8G8B8A8 && srcFormat == FMT_A8R8G8B8)
	{
		for(yy = 0; yy < height; yy++)
		{
			for(xx = 0; xx < width; xx++)
			{
				dest[xx * 4] = src[xx * 4 + 3];
				dest[xx * 4 + 1] = src[xx * 4];
				dest[xx * 4 + 2] = src[xx * 4 + 1];
				dest[xx * 4 + 3] = src[xx * 4 + 2];
			}
			dest += dstPitch;
			src += srcPitch;
		}
	}
	else if(dstFormat == FMT_A8R8G8B8 && srcFormat == FMT_L8)
	{
		for(yy = 0; yy < height; yy++)
		{
			for(xx = 0; xx < width; xx++)
			{
				dest[xx * 4] = src[xx];
				dest[xx * 4 + 1] = src[xx];
				dest[xx * 4 + 2] = src[xx];
				dest[xx * 4 + 3] = (char)255;
			}
			dest += dstPitch;
			src += srcPitch;
		}
	}
	else if(dstFormat == FMT_A8R8G8B8 && srcFormat == FMT_A8)
	{
		for(yy = 0; yy < height; yy++)
		{
			for(xx = 0; xx < width; xx++)
			{
				dest[xx * 4] = 0;
				dest[xx * 4 + 1] = 0;
				dest[xx * 4 + 2] = 0;
				dest[xx * 4 + 3] = src[xx];
			}
			dest += dstPitch;
			src += srcPitch;
		}
	}
	else if(dstFormat == FMT_A8R8G8B8 && srcFormat == FMT_AL8)
	{
		for(yy = 0; yy < height; yy++)
		{
			for(xx = 0; xx < width; xx++)
			{
				dest[xx * 4] = src[xx];
				dest[xx * 4 + 1] = src[xx];
				dest[xx * 4 + 2] = src[xx];
				dest[xx * 4 + 3] = src[xx];
			}
			dest += dstPitch;
			src += srcPitch;
		}
	}
	else if(dstFormat == FMT_A8R8G8B8 && srcFormat == FMT_AL_64F)
	{
		for(yy = 0; yy < height; yy++)
		{
			double *srcData = (double*)src;

			for(xx = 0; xx < width; xx++)
			{
				double a = srcData[xx] * 255.0;
				int c;

				if(a < 0.0)
					c = 0;
				if(a > 255.0)
					c = 255;
				else
					c = (int)a;
				dest[xx * 4] = c;
				dest[xx * 4 + 1] = c;
				dest[xx * 4 + 2] = c;
				dest[xx * 4 + 3] = c;
			}
			dest += dstPitch;
			src += srcPitch;
		}
	}
	else if(dstFormat == FMT_R8G8B8 && srcFormat == FMT_A8R8G8B8)
	{
		for(yy = 0; yy < height; yy++)
		{
			for(xx = 0; xx < width; xx++)
			{
				dest[xx * 3] = src[xx * 4];
				dest[xx * 3 + 1] = src[xx * 4 + 1];
				dest[xx * 3 + 2] = src[xx * 4 + 2];
			}
			dest += dstPitch;
			src += srcPitch;
		}
	}
	else if(dstFormat == FMT_ABGR_16F && srcFormat == FMT_AL_64F)
	{
		for(yy = 0; yy < height; yy++)
		{
			double *srcData = (double*)src;
			short *dstData = (short*)dest;

			for(xx = 0; xx < width; xx++)
			{
				short s = convert_32f_16f((float)srcData[xx]);

				dstData[xx * 4] = s;
				dstData[xx * 4 + 1] = s;
				dstData[xx * 4 + 2] = s;
				dstData[xx * 4 + 3] = s;
			}
			dest += dstPitch;
			src += srcPitch;
		}
	}
	else if(dstFormat == FMT_ABGR_16F && srcFormat == FMT_AL_32F)
	{
		for(yy = 0; yy < height; yy++)
		{
			float *srcData = (float*)src;
			short *dstData = (short*)dest;

			for(xx = 0; xx < width; xx++)
			{
				short s = convert_32f_16f(srcData[xx]);

				dstData[xx * 4] = s;
				dstData[xx * 4 + 1] = s;
				dstData[xx * 4 + 2] = s;
				dstData[xx * 4 + 3] = s;
			}
			dest += dstPitch;
			src += srcPitch;
		}
	}
	else if(dstFormat == FMT_ABGR_32F && srcFormat == FMT_AL_64F)
	{
		for(yy = 0; yy < height; yy++)
		{
			double *srcData = (double*)src;
			float *dstData = (float*)dest;

			for(xx = 0; xx < width; xx++)
			{
				dstData[xx * 4] = (float)srcData[xx];
				dstData[xx * 4 + 1] = (float)srcData[xx];
				dstData[xx * 4 + 2] = (float)srcData[xx];
				dstData[xx * 4 + 3] = (float)srcData[xx];
			}
			dest += dstPitch;
			src += srcPitch;
		}
	}
	else
		return false;
	return true;
}

//
// CriticalSection
//

CriticalSection::CriticalSection()
{
	lockCount = 0;
#ifdef WIN32
	InitializeCriticalSection(&criticalSection);
#else
	pthread_mutexattr_t a;
	
	pthread_mutexattr_init(&a);
	pthread_mutexattr_settype(&a, PTHREAD_MUTEX_RECURSIVE);
	pthread_mutex_init(&mutex, &a);
	pthread_mutexattr_destroy(&a);
#endif
}

CriticalSection::~CriticalSection()
{
#ifdef WIN32
	DeleteCriticalSection(&criticalSection);
#else
#endif
}

int CriticalSection::enterCriticalSection()
{
#ifdef WIN32
	EnterCriticalSection(&criticalSection);
#else
	pthread_mutex_lock(&mutex);
#endif
	lockCount++;
	if(lockCount == 1)
		onEnter();
	return lockCount;
}

int CriticalSection::leaveCriticalSection()
{
	lockCount--;
	if(lockCount == 0)
		onLeave();
#ifdef WIN32
	LeaveCriticalSection(&criticalSection);
#else
	pthread_mutex_unlock(&mutex);
#endif
	return lockCount;
}

void CriticalSection::onEnter()
{
}

void CriticalSection::onLeave()
{
}

